#pragma once

#include <iostream>
#include <string>
#include <functional>

#include <cstring>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>

#include "uncopyable.hpp"
#include "Log.hpp"
#include "Inet.hpp"

using namespace log_ddsm;

enum
{
    SOCK_CREAT_FAIL = 1,
    BIND_FAIL,
    LISTEN_FAIL,

    MSSIZE = 1024
};

// static不暴露给文件外部
const static int gport = 8889;
const static int gsockfd = -1;
const static int gblcklog = 8;

// Tcp服务器，接受用户发送的信息，发回消息
class TcpServer : public uncopyable
{
    // 回调函数的类型
    using com_service_t = std::function<void(int rwfd, const Inet &addr)>;

public:
    //在构造的时候，传入回调函数即可，实现高度解耦
    TcpServer(com_service_t service, uint16_t localport = gport)
        : _locatport(localport), _listenfd(gsockfd), _isrunning(false), _service(service)
    {
    }
    void Init()
    {
        // 1.以tcp方式创建网络套接字
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            LOG(FATAL, "socket create fail!\n");
            exit(SOCK_CREAT_FAIL);
        }
        LOG(DEBUG, "create sockfd success\n");
        // 2.填入sockaddr结构体并绑定
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_locatport);
        // 本主机的任意ip地址都可访问此localport
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listenfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind fail!\n");
            exit(BIND_FAIL);
        }
        LOG(DEBUG, "bind success\n");
        // 3.tcp是面向连接的，tcp需要不断的能够获取连接
        n = listen(_listenfd, gblcklog);
        if (n < 0)
        {
            LOG(FATAL, "listen fail");
            exit(LISTEN_FAIL);
        }
        LOG(DEBUG, "listen success\n");
    }
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            /// 多进程的处理方法.1
            // 父进程忽略子进程的退出信号,父进程不再等待子进程退出
            // signal(SIGCHLD,SIG_IGN);

            // 4.通过listenfd获取rwfd,之后提供服务
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int rwfd = accept(_listenfd, (struct sockaddr *)&client, &len);
            if (rwfd < 0)
            {
                // 获取读写fd失败，对整体获取无影响；如果失败，则再次尝试获取即可
                LOG(WARNING, "rwfd get (accept) fail\n");
                continue;
            }
            // 想要打印客户端信息——需要构造INET类(用于维护网络转化为主机的class)
            Inet peer(client);
            LOG(DEBUG, "rwfd get (accept) success ,client info: %s\n", peer.UniqueName().c_str());

            // 5.提供服务(服务器需要能够同时为多个客户端提供服务)

            /* 使用多线程解决服务器无法同时服务多个客户端的问题----原生线程的使用 */
            pthread_t tid;
            ThreadData *td = new ThreadData(this, rwfd, peer);
            pthread_create(&tid, nullptr, Excute, (void *)td);
        }
        _isrunning = false;
    }
    // 使用多线程解决服务器无法同时服务多个客户端的问题----原生线程的使用
    // 创建的目的是便于传递参数
    struct ThreadData
    {
        TcpServer *_self;
        int _rwfd;
        Inet _addr;

        ThreadData(TcpServer *self, int rwfd, const Inet &addr)
            : _self(self), _rwfd(rwfd), _addr(addr)
        {
        }
        ~ThreadData()
        {
            close(_rwfd);
        }
    };

    static void *Excute(void *args) // 防止this指针干扰函数类型
    {
        // 新线程解除与主线程的关系，主线程不再需要等待新线程
        pthread_detach(pthread_self());
        // 目的是调用Service---static内部无法获取对象，需要传递进来
        ThreadData *ptd = static_cast<ThreadData *>(args);
        // 设置回调函数
        ptd->_self->_service(ptd->_rwfd, ptd->_addr);
        delete ptd;
        return nullptr;
    }

    // void Service(int rwfd, const Inet &peer)
    // {
    //     // 长服务
    //     while (true)
    //     {
    //         char cmdmsg[MSSIZE] = {0}; // 缓冲区大小MSSIZE
    //         ssize_t n = read(rwfd, cmdmsg, sizeof(cmdmsg) - 1);
    //         if (n > 0)
    //         {
    //             cmdmsg[n] = '\0';
    //             std::cout << "get message from " << "[" << peer.UniqueName().c_str() << "]" << cmdmsg << std::endl;
    //             // 提供服务
    //             //  std::string result = ;
    //             //  write();
    //         }
    //         else if (n == 0) // 代表客户端结束
    //         {
    //             LOG(INFO, "client [%s] quit/EOF\n", peer.UniqueName().c_str());
    //             break;
    //         }
    //         else
    //         {
    //             LOG(ERROR, "client [%s] read fail!\n", peer.UniqueName().c_str());
    //             break;
    //         }
    //     }
    //     close(rwfd);
    // }
    ~TcpServer() {}

private:
    uint16_t _locatport; // 端口
    int _listenfd;       // 套接字
    bool _isrunning;
    com_service_t _service; // 回调函数
};